package com.nuorui.module.system.service.impl;

import cn.dev33.satoken.stp.StpInterface;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.nuorui.common.constant.RequestConst;
import com.nuorui.common.core.domain.RequestUser;
import com.nuorui.common.core.domain.ResponseDTO;
import com.nuorui.common.core.code.UserErrorCode;
import com.nuorui.common.core.enums.ConfigKeyEnum;
import com.nuorui.common.core.enums.LoginDeviceEnum;
import com.nuorui.common.core.enums.UserTypeEnum;
import com.nuorui.common.utils.SmartEnumUtil;
import com.nuorui.common.utils.SmartIpUtil;
import com.nuorui.module.supper.apiencrypt.service.ApiEncryptService;
import com.nuorui.module.supper.loginlog.LoginLogResultEnum;
import com.nuorui.module.supper.loginlog.LoginLogService;
import com.nuorui.module.supper.loginlog.domain.LoginLogEntity;
import com.nuorui.module.supper.securityprotect.domain.LoginFailEntity;
import com.nuorui.module.supper.securityprotect.service.SecurityLoginService;
import com.nuorui.module.supper.securityprotect.service.SecurityPasswordService;
import com.nuorui.module.system.mapper.RoleUserMapper;
import com.nuorui.module.system.mapper.UserMapper;
import com.nuorui.module.system.domain.form.LoginForm;
import com.nuorui.common.core.domain.LoginUser;
import com.nuorui.module.system.domain.entity.UserEntity;
import com.nuorui.module.system.domain.vo.LoginResultVO;
import com.nuorui.module.system.domain.vo.MenuVO;
import com.nuorui.module.system.domain.vo.RoleVO;
import com.nuorui.module.system.domain.vo.UserPermission;
import com.nuorui.module.system.manager.LoginManager;
import com.nuorui.module.system.service.*;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 类描述
 *
 * @author: Fred
 * @email 453086@qq.com
 * @create: 2025-06-06 14:46
 */
@Slf4j
@Service
@AllArgsConstructor
public class LoginServiceImpl implements LoginService, StpInterface {

    /**
     * 万能密码的 sa token loginId 前缀
     */
    private static final String SUPER_PASSWORD_LOGIN_ID_PREFIX = "S";

    private final RoleMenuService roleMenuService;
    private final ConfigService configService;

    private final ApiEncryptService apiEncryptService;
    private final SecurityLoginService securityLoginService;
    private final LoginLogService loginLogService;

    private final UserMapper  userMapper;
    private final RoleUserMapper roleUserMapper;

    private final LoginManager loginManager;


    @Override
    public ResponseDTO<LoginResultVO> login(LoginForm loginForm, String ip, String userAgent) {

        LoginDeviceEnum loginDeviceEnum = SmartEnumUtil.getEnumByValue(loginForm.getLoginDevice(), LoginDeviceEnum.class);
        if (loginDeviceEnum == null) {
            return ResponseDTO.userErrorParam("登录设备暂不支持！");
        }

        // 验证登录名
        UserEntity userEntity = userMapper.getByLoginName(loginForm.getLoginName());
        if (null == userEntity) {
            return ResponseDTO.userErrorParam("登录名或密码错误！");
        }

        // 验证账号状态
        if (userEntity.getIsDisabled()) {
            return ResponseDTO.userErrorParam("您的账号已被禁用,请联系工作人员！");
        }

        // 解密前端加密的密码
        String requestPassword = apiEncryptService.decrypt(loginForm.getPassword());
        // 验证密码 是否为万能密码
        String superPassword = configService.getConfigValue(ConfigKeyEnum.SUPER_PASSWORD);
        boolean isSuperPassword = superPassword.equals(requestPassword);

        // 万能密码特殊操作
        if (isSuperPassword) {
            // 对于万能密码：受限制sa token 要求loginId唯一，万能密码只能插入一段uuid
            String saTokenLoginId = SUPER_PASSWORD_LOGIN_ID_PREFIX + StrUtil.COLON + UUID.randomUUID().toString().replace("-", "") + StrUtil.COLON + userEntity.getUserId();
            // 万能密码登录只能登录30分钟
            StpUtil.login(saTokenLoginId, 1800);
        } else {

            // 按照等保登录要求，进行登录失败次数校验
            ResponseDTO<LoginFailEntity> loginFailEntityResponseDTO = securityLoginService.checkLogin(userEntity.getUserId(), UserTypeEnum.ADMIN_EMPLOYEE);
            if (!loginFailEntityResponseDTO.getOk()) {
                return ResponseDTO.error(loginFailEntityResponseDTO);
            }

            // 密码错误
            if (!SecurityPasswordService.matchesPwd(requestPassword, userEntity.getLoginPwd())) {
                // 记录登录失败
                // 记录等级保护次数
                String msg = securityLoginService.recordLoginFail(userEntity.getUserId(), UserTypeEnum.ADMIN_EMPLOYEE, userEntity.getLoginName(), loginFailEntityResponseDTO.getData());
                return msg == null ? ResponseDTO.userErrorParam("登录名或密码错误！") : ResponseDTO.error(UserErrorCode.LOGIN_FAIL_WILL_LOCK, msg);
            }
            String saTokenLoginId = UserTypeEnum.ADMIN_EMPLOYEE.getValue() + StrUtil.COLON + userEntity.getUserId();
            // 登录
            StpUtil.login(saTokenLoginId, String.valueOf(loginDeviceEnum.getDesc()));

        }

        // 获取员工信息
        LoginUser loginUser = loginManager.loadLoginInfo(userEntity);

        // 移除登录失败
        securityLoginService.removeLoginFail(userEntity.getUserId(), UserTypeEnum.ADMIN_EMPLOYEE);

        // 获取登录结果信息
        String token = StpUtil.getTokenValue();
        LoginResultVO loginResultVO = getLoginResult(loginUser, token);

        //保存登录记录
        saveLoginLog(userEntity, ip, userAgent, isSuperPassword ? "万能密码登录" : StrUtil.EMPTY, LoginLogResultEnum.LOGIN_SUCCESS, loginDeviceEnum);

        // 设置 token
        loginResultVO.setToken(token);

        // 更新用户权限
        loginManager.loadUserPermission(userEntity.getUserId());

        return ResponseDTO.ok(loginResultVO);
    }


    /**
     * 获取登录结果信息
     */
    @Override
    public LoginResultVO getLoginResult(LoginUser loginUser, String token) {

        // 基础信息
        LoginResultVO loginResultVO = BeanUtil.copyProperties(loginUser, LoginResultVO.class);

        // 前端菜单和功能点清单
        List<RoleVO> roleList = roleUserMapper.selectRolesByUserId(loginUser.getUserId());
        List<MenuVO> menuAndPointsList = roleMenuService.getMenusByRoleIdList(roleList.stream().map(RoleVO::getRoleId).collect(Collectors.toList()), loginUser.getIsAdministrator());
        loginResultVO.setMenuList(menuAndPointsList);

        // 上次登录信息

        // 是否需要强制修改密码

        // 万能密码登录，则不需要设置强制修改密码
        String loginIdByToken = (String) StpUtil.getLoginIdByToken(token);
        if (loginIdByToken != null && loginIdByToken.startsWith(SUPER_PASSWORD_LOGIN_ID_PREFIX)) {
            loginResultVO.setNeedUpdatePwdFlag(false);
        }

        return loginResultVO;
    }

    /**
     * 根据登陆token 获取员请求工信息
     */
    @Override
    public LoginUser getLoginUser(String loginId, HttpServletRequest request) {
        if (StrUtil.isBlank(loginId)) {
            return null;
        }

        Long requestUserId = getUserIdByLoginId(loginId);
        if (requestUserId == null) {
            return null;
        }


        LoginUser loginUser = loginManager.getRequestEmployee(requestUserId);

        // 更新请求ip和user agent
        loginUser.setUserAgent(ServletUtil.getHeaderIgnoreCase(request, RequestConst.Header.USER_AGENT));
        loginUser.setIp(ServletUtil.getClientIP(request));

        return loginUser;
    }

    /**
     * 根据 loginId 获取 员工id
     */
    private Long getUserIdByLoginId(String loginId) {
        try {
            // 如果是 万能密码 登录的用户
            String userIdStr;
            if (loginId.startsWith(SUPER_PASSWORD_LOGIN_ID_PREFIX)) {
                userIdStr = loginId.split(StrUtil.COLON)[2];
            } else {
                userIdStr = loginId.substring(2);
            }
            return Long.parseLong(userIdStr);
        } catch (Exception e) {
            log.error("loginId parse error , loginId : {}", loginId, e);
            return null;
        }
    }

    /**
     * 退出登录
     */
    @Override
    public ResponseDTO<String> logout(RequestUser requestUser) {

        // sa token 登出
        StpUtil.logout();

        // 清空登录信息缓存
        loginManager.clear();

        //保存登出日志
        LoginLogEntity loginEntity = LoginLogEntity.builder()
                .userId(requestUser.getUserId())
                .userType(requestUser.getUserType().getValue())
                .userName(requestUser.getUserName())
                .userAgent(requestUser.getUserAgent())
                .loginIp(requestUser.getIp())
                .loginIpRegion(SmartIpUtil.getRegion(requestUser.getIp()))
                .loginResult(LoginLogResultEnum.LOGIN_OUT.getValue())
                .createTime(LocalDateTime.now())
                .build();
        loginLogService.log(loginEntity);

        return ResponseDTO.ok();
    }

    /**
     * 保存登录日志
     */
    private void saveLoginLog(UserEntity userEntity, String ip, String userAgent, String remark, LoginLogResultEnum result, LoginDeviceEnum loginDeviceEnum) {
        LoginLogEntity loginEntity = LoginLogEntity.builder()
                .userId(userEntity.getUserId())
                .userType(UserTypeEnum.ADMIN_EMPLOYEE.getValue())
                .userName(userEntity.getActualName())
                .userAgent(userAgent)
                .loginIp(ip)
                .loginIpRegion(SmartIpUtil.getRegion(ip))
                .remark(remark)
                .loginDevice(loginDeviceEnum.getDesc())
                .loginResult(result.getValue())
                .createTime(LocalDateTime.now())
                .build();
        loginLogService.log(loginEntity);
    }


    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        Long userId = getUserIdByLoginId((String) loginId);
        if (userId == null) {
            return Collections.emptyList();
        }

        UserPermission userPermission = loginManager.getUserPermission(userId);
        return userPermission.getPermissionList();
    }

    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        Long userId = getUserIdByLoginId((String) loginId);
        if (userId == null) {
            return Collections.emptyList();
        }

        UserPermission userPermission = loginManager.getUserPermission(userId);
        return userPermission.getRoleList();
    }

    @Override
    public void clearLoginUserCache(Long userId) {
        loginManager.clear();
    }
}
